using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Rocuna.GameEngine.Graphics.Components;

namespace Rocuna.GameEngine.Extension.WP7.Graphics
{
    public class SkyBoxTemp : SkyBoxBase
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="SkyBoxBase"/> class.
        /// </summary>
        /// <param name="cube">The cube.</param>
        /// <param name="game">The Game that the game component should be attached to.</param>
        public SkyBoxTemp(TextureCube cube, Game game)
            : base(game)
        {
            Cube = cube;
            CubeFaces = new Texture2D[6];
            StripTexturesFromCube();
            CreateGraphic(1200);
            InitializeData(Game.GraphicsDevice);
        }

        #region Properties

        /// <summary>
        /// Gets or sets the cube.
        /// </summary>
        /// <value>
        /// The cube.
        /// </value>
        public TextureCube Cube { get; set; }

        /// <summary>
        /// Gets or sets the cube faces.
        /// </summary>
        /// <value>
        /// The cube faces.
        /// </value>
        public Texture2D[] CubeFaces { get; set; }

        /// <summary>
        /// Gets or sets the vertex buffer.
        /// </summary>
        /// <value>
        /// The vertex buffer.
        /// </value>
        public VertexBuffer VertexBuffer { get; set; }

        /// <summary>
        /// Gets or sets the index buffer.
        /// </summary>
        /// <value>
        /// The index buffer.
        /// </value>
        public IndexBuffer IndexBuffer { get; set; }

        /// <summary>
        /// Gets or sets the effect.
        /// </summary>
        /// <value>
        /// The effect.
        /// </value>
        public BasicEffect Effect { get; set; }

        #endregion

        #region Fields

        private List<VertexPositionNormalTexture> _vertices = new List<VertexPositionNormalTexture>();
        //private List<VertexPositionColor> _vertices = new List<VertexPositionColor>();

        private List<ushort> _indices = new List<ushort>();

        #endregion

        #region Overrides of Drawable game component

        public override void Draw(GameTime gameTime)
        {
            Game.GraphicsDevice.Clear(Color.Green);
            Game.GraphicsDevice.SetVertexBuffer(VertexBuffer);
            Game.GraphicsDevice.Indices = IndexBuffer;
            Effect.Texture = CubeFaces[0];
            Effect.CurrentTechnique.Passes[0].Apply();
            Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, _vertices.Count, 0, 2);

            base.Draw(gameTime);
        }


        //Approved.
        protected void StripTexturesFromCube()
        {
            var pixelArray = new Color[Cube.Size * Cube.Size];

            for (int s = 0; s < CubeFaces.Length; s++)
            {
                CubeFaces[s] = new Texture2D(Game.GraphicsDevice, Cube.Size, Cube.Size, false, SurfaceFormat.Color);
                switch (s)
                {
                    case 0:
                        Cube.GetData<Color>(CubeMapFace.PositiveX, pixelArray);
                        CubeFaces[s].SetData<Color>(pixelArray);
                        break;
                    case 1:
                        Cube.GetData(CubeMapFace.NegativeX, pixelArray);
                        CubeFaces[s].SetData(pixelArray);
                        break;
                    case 2:
                        Cube.GetData(CubeMapFace.PositiveY, pixelArray);
                        CubeFaces[s].SetData(pixelArray);
                        break;
                    case 3:
                        Cube.GetData(CubeMapFace.NegativeY, pixelArray);
                        CubeFaces[s].SetData(pixelArray);
                        break;
                    case 4:
                        Cube.GetData(CubeMapFace.PositiveZ, pixelArray);
                        CubeFaces[s].SetData(pixelArray);
                        break;
                    case 5:
                        Cube.GetData(CubeMapFace.NegativeZ, pixelArray);
                        CubeFaces[s].SetData(pixelArray);
                        break;
                }
            }
        }

        protected void CreateGraphic(float size)
        {
            Vector3[] normals = {
                                    Vector3.Right,
                                    Vector3.Left,
                                    Vector3.Up,
                                    Vector3.Down,
                                    Vector3.Backward,
                                    Vector3.Forward,
                                };
            Vector2[] textureCoordinates = {
                                               Vector2.One, Vector2.UnitY, Vector2.Zero, Vector2.UnitX,
                                               Vector2.Zero, Vector2.UnitX, Vector2.One, Vector2.UnitY,
                                               Vector2.Zero, Vector2.UnitX, Vector2.One, Vector2.UnitY,
                                               Vector2.Zero, Vector2.UnitX, Vector2.One, Vector2.UnitY,
                                               Vector2.UnitY, Vector2.Zero, Vector2.UnitX, Vector2.One,
                                               Vector2.UnitY, Vector2.Zero, Vector2.UnitX, Vector2.One,
                                           };
            var index = 0;
            foreach (var normal in normals)
            {
                var side1 = new Vector3(normal.Z, normal.X, normal.Y);
                var side2 = Vector3.Cross(normal, side1);

                AddIndex(CurrentVertex + 0);
                AddIndex(CurrentVertex + 1);
                AddIndex(CurrentVertex + 2);

                AddIndex(CurrentVertex + 0);
                AddIndex(CurrentVertex + 2);
                AddIndex(CurrentVertex + 3);

                AddVertex((normal - side1 - side2) * size / 2, normal, textureCoordinates[index++]);
                AddVertex((normal - side1 + side2) * size / 2, normal, textureCoordinates[index++]);
                AddVertex((normal + side1 + side2) * size / 2, normal, textureCoordinates[index++]);
                AddVertex((normal + side1 - side2) * size / 2, normal, textureCoordinates[index++]);
                //AddVertex((normal + side1 - side2) * size / 2, Color.White);
                //AddVertex((normal + side1 - side2) * size / 2, Color.Blue);
                //AddVertex((normal + side1 - side2) * size / 2, Color.Red);
                //AddVertex((normal + side1 - side2) * size / 2, Color.Black);
            }
        }

        protected void InitializeData(GraphicsDevice graphicsDevice)
        {
            VertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionNormalTexture), _vertices.Count,
                BufferUsage.None);
            VertexBuffer.SetData(_vertices.ToArray());

            // Create an index buffer, and copy our index data into it.
            IndexBuffer = new IndexBuffer(graphicsDevice, typeof(ushort),
                                          _indices.Count, BufferUsage.None);

            IndexBuffer.SetData(_indices.ToArray());

            // Create a BasicEffect, which will be used to render the primitive.
            Effect = new BasicEffect(graphicsDevice);

            Effect.TextureEnabled = true;

            Effect.EnableDefaultLighting();
        }

        #endregion

        #region setter methods

        protected void AddVertex(Vector3 position, Vector3 normal, Vector2 textureCoordinates)
        {
            _vertices.Add(new VertexPositionNormalTexture(position, normal, textureCoordinates));
        }

        //protected void AddVertex(Vector3 position, Color color)
        //{
        //    _vertices.Add(new VertexPositionColor(position, color));
        //}

        protected void AddIndex(int index)
        {
            if (index > ushort.MaxValue)
                throw new ArgumentOutOfRangeException("index");

            _indices.Add((ushort)index);
        }

        protected int CurrentVertex
        {
            get { return _vertices.Count; }
        }

        #endregion

    }
}